{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "with 上下文管理器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Classical File open\n",
    "file = open('path/to/file', 'r')\n",
    "try:\n",
    "    data = file.read()\n",
    "finally:\n",
    "    file.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# with context manager\n",
    "with open('path/to/file', 'r') as file:\n",
    "    # 不论是正常退出还是异常退出这里离开了with块都会结束\n",
    "    # 无需手动close"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 用class实现上下文管理器\n",
    "class MyOpen:\n",
    "    def __init__(self, path):\n",
    "        print('Entering constructor of MyOpen')\n",
    "        self.file_path = path\n",
    "    def __enter__(self):\n",
    "        print('Entering __enter__')\n",
    "        return self.file_path\n",
    "    def __exit__(self, exc_type, exc_val, exc_tb):\n",
    "        print('Entering __exit__')\n",
    "        if exc_type is not None:\n",
    "            # 发生异常的时候会执行\n",
    "            print('Caught a Error')\n",
    "            # 没有异常则None\n",
    "            print(exc_type, exc_val, exc_tb)\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Entering constructor of MyOpen\n",
      "Entering __enter__\n",
      "The value of file is {file}\n",
      "Entering __exit__\n",
      "None None None\n"
     ]
    }
   ],
   "source": [
    "with MyOpen('mytext.txt') as file:\n",
    "    print('The value of file is {file}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Entering constructor of MyOpen\n",
      "Entering __enter__\n",
      "Entering __exit__\n",
      "Caught a Error\n",
      "<class 'ValueError'> Something wrong <traceback object at 0x7f142fc5c080>\n"
     ]
    }
   ],
   "source": [
    "with MyOpen('mytext.txt') as file:\n",
    "    raise ValueError('Something wrong')\n",
    "    print('The value of file is {file}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "from contextlib import contextmanager\n",
    "\n",
    "# 一种特殊的装饰器,它让一个生成器函数转化为一个上下文管理器\n",
    "# yield之前的代码将会在进入with的时候执行\n",
    "# yiled之后的代码将会在离开with的时候执行\n",
    "@contextmanager\n",
    "def my_open(path):\n",
    "    print('Entering my_open')\n",
    "    try:\n",
    "        yield path\n",
    "    finally:\n",
    "        print('Exiting my_open')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Entering my_open\n",
      "The value of file is {file}\n",
      "Exiting my_open\n"
     ]
    }
   ],
   "source": [
    "with my_open('mytext.txt') as file:\n",
    "    print('The value of file is {file}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用yield的生成器函数做成的上下文管理器还有一个好处就是逻辑判断会容易一些\n",
    "from contextlib import contextmanager\n",
    "\n",
    "@contextmanager\n",
    "def my_open(path, special=False):\n",
    "    print('Entering my_open')\n",
    "    if special:\n",
    "        print('Opening special handling')\n",
    "        try:\n",
    "            yield\n",
    "            print('Closing special handling')\n",
    "        finally:\n",
    "            print('Exiting special my_open')\n",
    "    else:\n",
    "        try:\n",
    "            yield\n",
    "        finally:\n",
    "            print('Exiting normal my_open')\n",
    "\n",
    "# 使用生成器的上下文管理器\n",
    "with my_open('some_path', special=True) as path:\n",
    "    print(f'Inside with block, path is {path}')\n",
    "\n",
    "with my_open('some_path', special=False) as path:\n",
    "    print(f'Inside with block, path is {path}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 相反的,使用类就不是那么容易了\n",
    "class SpecialFile:\n",
    "    def __init__(self, path, special=False):\n",
    "        self.path = path\n",
    "        self.special = special\n",
    "\n",
    "    def __enter__(self):\n",
    "        print('Entering SpecialFile')\n",
    "        if self.special:\n",
    "            print('Opening special handling')\n",
    "        return self.path\n",
    "\n",
    "    def __exit__(self, exc_type, exc_val, exc_tb):\n",
    "        if self.special:\n",
    "            print('Closing special handling')\n",
    "        print('Exiting SpecialFile')\n",
    "\n",
    "# 使用类实现的上下文管理器\n",
    "with SpecialFile('some_path', special=True):\n",
    "    print(f'Inside with block, path is {path}')\n",
    "\n",
    "with SpecialFile('some_path', special=False):\n",
    "    print(f'Inside with block, path is {path}')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "mathmodal",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
